from numpy import array, concatenate, inner
from util import sign, dot
from plot import plotter
from time import time


""" Voted Perceptron algorithm (aka votedron)

    We construct our perceptron in the way it stores instance vectors
on which it have mistaken. Thus we can use any kernel function
instead of dot product. We added a list of counters that counts
a number each weight vector survives. The counters don't affect
the algorithm. We use them for voted style prediction. 
"""

class votedron:

    """ Initialize the algorithm with initial weight vector
            and function for dot product
    """    
    def __init__(self, w_init, dot = dot, alpha = 1):

        self.alpha = alpha
        self.Xs = [array(w_init)]
        self.w0 = [0]
        self.count = [1]
        self.dot = dot
        self.i = 0


    """ Train the algorithm with given list of instances (see instance.py)
            and for given number of seconds
        Returns the number of instances 
    """  
    def train(self, instances = None, num_seconds = 1):
        """ Train the algorithm with given list of instances (see instance.py)
                and for given number of seconds
            Returns the number of instances that had the time to procces
        """  
        
        trained = 0
        start = time()

        while(1):
            
            x = instances[self.i % len(instances)]
            self.i += 1
            y = self.classify(x.features)
            if(y != x.label):
                """mistake"""
                self.w0.append(self.w0[-1] - self.alpha * x.label) 
                self.Xs.append(self.alpha * x.label * x.features)
                self.count.append(1)  

            else:
                """ correct classification"""
                self.count[-1] += 1
            trained += 1
            if(self.i % 50 == 0):
                if (time() >= start + num_seconds): return trained
    
        
    def test(self, instances):
    
        """ Test the algorithm with given list of instances
        Returns: a number of misses
        Remark: for prediction uses member function classify
        """

        miss = 0
        for x in instances:
            
            y = self.classify(x.features)
            if(y != x.label): miss+=1
            
        return miss
    
    def classify(self, ins):

        sum = 0
        for x in self.Xs:
            
            sum += self.dot(x,ins)
            
        return sign(sum-self.dot(1,self.w0[-1]))
    
    def test_voted(self, instances):
        
        miss = 0
        for x in instances:
            
            y = self.classify_voted(x.features)
            if(y != x.label): miss+=1
            
        return miss

    def classify_voted(self, instance):

        sum = 0
        acc = 0
        for i in range(len(self.Xs)):
        
            sum += self.dot(instance, self.Xs[i])
            acc += self.count[i]*sign(sum-self.dot(1,self.w0[i]))
            
        return sign(acc)

